//
//  SymmetricEncryptionUtils.m
//  Login
//
//  Created by Sean on 4/12/10.
//  Copyright 2010 __MyCompanyName__. All rights reserved.
//

#import "SymmetricEncryptionUtils.h"


@implementation SymmetricEncryptionUtils

+ (NSData *)doCipher:(NSData *)plainText key:(NSData *)aSymmetricKey
			 context:(CCOperation)encryptOrDecrypt padding:(CCOptions *)pkcs7 
{
	CCCryptorStatus ccStatus = kCCSuccess;
	// Symmetric crypto reference.
	CCCryptorRef thisEncipher = NULL;
	// Cipher Text container.
	NSData * cipherOrPlainText = nil;
	// Pointer to output buffer.
	uint8_t * bufferPtr = NULL;
	// Total size of the buffer.
	size_t bufferPtrSize = 0;
	// Remaining bytes to be performed on.
	size_t remainingBytes = 0;
	// Number of bytes moved to buffer.
	size_t movedBytes = 0;
	// Length of plainText buffer.
	size_t plainTextBufferSize = 0;
	// Placeholder for total written.
	size_t totalBytesWritten = 0;
	// A friendly helper pointer.
	uint8_t * ptr;
	// Initialization vector; dummy in this case 0's.
	uint8_t iv[kCCBlockSizeAES128];
	memset((void *) iv, 0x0, (size_t) sizeof(iv));
	NSLog(@"doCipher: plaintext: %@", plainText);
	NSLog(@"doCipher: key length: %d", [aSymmetricKey length]);
	
	//if(plainText != nil)
	//		NSLog(@"PlainText object cannot be nil." );
	//	if(aSymmetricKey != nil)
	//		NSLog(@"Symmetric key object cannot be nil." );
	//	if(pkcs7 != NULL)
	//		NSLog(@"CCOptions * pkcs7 cannot be NULL." );
	//	if([aSymmetricKey length] == kCCKeySizeAES128)
	//		NSLog(@"Disjoint choices for key size." );
	
	
	plainTextBufferSize = [plainText length];
	
	//if(plainTextBufferSize > 0)
	//		NSLog(@"Empty plaintext passed in." );
	
	NSLog(@"pkcs7: %d", *pkcs7);
	// We don't want to toss padding on if we don't need to
	if(encryptOrDecrypt == kCCEncrypt) {
		if(*pkcs7 != kCCOptionECBMode) {
			if((plainTextBufferSize % kCCBlockSizeAES128) == 0) {
				*pkcs7 = 0x0000;
			} else {
				*pkcs7 = kCCOptionPKCS7Padding;
			}
		}
	} else if(encryptOrDecrypt != kCCDecrypt) {
		NSLog(@"Invalid CCOperation parameter [%d] for cipher context.", *pkcs7 );
	}
	// Create and Initialize the crypto reference.
	ccStatus = CCCryptorCreate(encryptOrDecrypt,
							   kCCAlgorithmAES128,
							   *pkcs7,
							   (const void *)[aSymmetricKey bytes],
							   kCCKeySizeAES128,
							   (const void *)iv,
							   &thisEncipher
							   );
	
	//if( ccStatus == kCCSuccess)
	//		NSLog(@"Problem creating the context, ccStatus == %d.", ccStatus );
	
	// Calculate byte block alignment for all calls through to and including final.
	bufferPtrSize = CCCryptorGetOutputLength(thisEncipher, plainTextBufferSize, true);
	// Allocate buffer.
	bufferPtr = malloc( bufferPtrSize * sizeof(uint8_t) );
	// Zero out buffer.
	memset((void *)bufferPtr, 0x0, bufferPtrSize);
	// Initialize some necessary book keeping.
	ptr = bufferPtr;
	// Set up initial size.
	remainingBytes = bufferPtrSize;
	// Actually perform the encryption or decryption.
	ccStatus = CCCryptorUpdate(thisEncipher,
							   (const void *) [plainText bytes],
							   plainTextBufferSize,
							   ptr,
							   remainingBytes,
							   &movedBytes
							   );
	
	//if(ccStatus == kCCSuccess)
	//		NSLog(@"Problem with CCCryptorUpdate, ccStatus == %d.", ccStatus );
	
	// Handle book keeping.
	ptr += movedBytes;
	remainingBytes -= movedBytes;
	totalBytesWritten += movedBytes;
	
	// Finalize everything to the output buffer.
	ccStatus = CCCryptorFinal(thisEncipher,
							  ptr,
							  remainingBytes,
							  &movedBytes
							  );
	totalBytesWritten += movedBytes;
	if(thisEncipher) {
		(void) CCCryptorRelease(thisEncipher);
		thisEncipher = NULL;
	}
	
	//if( ccStatus == kCCSuccess)
	//		NSLog(@"Problem with encipherment ccStatus == %d", ccStatus );
	
	if (ccStatus == kCCSuccess)
		cipherOrPlainText = [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)totalBytesWritten];
	else
		cipherOrPlainText = nil;
	if(bufferPtr) free(bufferPtr);
	return cipherOrPlainText;
	
}


+ (NSData *)encrypt:(NSData*)plainText key:(NSData *)aSymmetricKey padding:(CCOptions *)pkcs7 {
	return [SymmetricEncryptionUtils doCipher:plainText key:aSymmetricKey context:kCCEncrypt padding:pkcs7];
}


+ (NSData *)decrypt:(NSData *)plainText key:(NSData *)aSymmetricKey padding:(CCOptions *)pkcs7 {
	return [SymmetricEncryptionUtils doCipher:plainText key:aSymmetricKey context:kCCDecrypt padding:pkcs7];
}


@end
